﻿using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Security.Principal;
using System.ServiceModel.DomainServices.Client.ApplicationServices;
using IssueVision.Data.Web;
using IssueVision.Common;

namespace IssueVision.Model
{
    [Export(typeof(IAuthenticationModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class AuthenticationModel : IAuthenticationModel
    {
        #region "Private Data"
        private AuthenticationService _service;
        #endregion "Private Data"

        #region "Protected Propertes"
        protected AuthenticationService AuthService
        {
            get
            {
                if (_service == null)
                {
                    // This will enable WebContext to dsicover Authenticaion Context's from a CLass Library 
                    ((WebAuthenticationService)WebContext.Current.Authentication).DomainContext = new AuthenticationContext();
                    _service = WebContext.Current.Authentication;

                    ((INotifyPropertyChanged)_service).PropertyChanged += _service_PropertyChanged;
                    _service.LoggedIn += _service_LoggedIn;
                    _service.LoggedOut += _service_LoggedOut;
                }

                return _service;
            }
        }
        #endregion "Protected Propertes"

        #region "IAuthenticationModel Interface implementation"
        /// <summary>
        /// This will automatically authenticate a user when 
        /// the user chose "Keep me signed in" on a previous login attempt
        /// </summary>
        public void LoadUserAsync()
        {
            AuthService.LoadUser(LoadUserOperation_Completed, null);
        }

        /// <summary>
        /// Authenticate a user with user name and password
        /// </summary>
        /// <param name="loginParameters"></param>
        public void LoginAsync(LoginParameters loginParameters)
        {
            AuthService.Login(loginParameters, LoginOperation_Completed, null);
        }

        /// <summary>
        /// Logout
        /// </summary>
        public void LogoutAsync()
        {
            AuthService.Logout(LogoutOperation_Completed, null);
        }

        /// <summary>
        /// A principal object represents the security context of the user
        /// on whose behalf the code is running, including that user's
        /// identity (IIdentity) and any roles to which they belong.
        /// </summary>
        public IPrincipal User
        {
            get { return AuthService.User; }
        }

        /// <summary>
        /// True if there is an operation (LoadUserAsync, 
        /// LoginAsync, or LogoutAsync) in progress; otherwise, false
        /// </summary>
        public Boolean IsBusy
        {
            get { return AuthService.IsBusy; }
        }

        /// <summary>
        /// true if there is an operation in progress; otherwise, false
        /// </summary>
        public bool IsLoadingUser
        {
            get { return AuthService.IsLoadingUser; }
        }

        /// <summary>
        /// true if there is an operation in progress; otherwise, false
        /// </summary>
        public bool IsLoggingIn
        {
            get { return AuthService.IsLoggingIn; }
        }

        /// <summary>
        /// true if there is an operation in progress; otherwise, false
        /// </summary>
        public bool IsLoggingOut
        {
            get { return AuthService.IsLoggingOut; }
        }

        /// <summary>
        /// true if there is an operation in progress; otherwise, false
        /// </summary>
        public bool IsSavingUser
        {
            get { return AuthService.IsSavingUser; }
        }

        public event EventHandler<LoadUserOperationEventArgs> LoadUserComplete;
        public event EventHandler<LoginOperationEventArgs> LoginComplete;
        public event EventHandler<LogoutOperationEventArgs> LogoutComplete;
        public event EventHandler<AuthenticationEventArgs> AuthenticationChanged;
        #endregion "IAuthenticationModel Interface implementation"

        #region "INotifyPropertyChanged Interface implementation"
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion "INotifyPropertyChanged Interface implementation"

        #region "Private Methods"
        private void _service_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "IsBusy":
                    OnPropertyChanged("IsBusy");
                    break;
                case "IsLoadingUser":
                    OnPropertyChanged("IsLoadingUser");
                    break;
                case "IsLoggingIn":
                    OnPropertyChanged("IsLoggingIn");
                    break;
                case "IsLoggingOut":
                    OnPropertyChanged("IsLoggingOut");
                    break;
                case "IsSavingUser":
                    OnPropertyChanged("IsSavingUser");
                    break;
                case "User":
                    OnPropertyChanged("User");
                    break;
            }
        }

        private void _service_LoggedIn(object sender, AuthenticationEventArgs e)
        {
            if (AuthenticationChanged != null)
                AuthenticationChanged(this, e);
        }

        private void _service_LoggedOut(object sender, AuthenticationEventArgs e)
        {
            if (AuthenticationChanged != null)
                AuthenticationChanged(this, e);
        }

        private void LoadUserOperation_Completed(LoadUserOperation loadUserOperation)
        {
            if (loadUserOperation.HasError)
            {
                if (LoadUserComplete != null)
                    LoadUserComplete(this, new LoadUserOperationEventArgs(loadUserOperation, loadUserOperation.Error));
                loadUserOperation.MarkErrorAsHandled();
            }
            else
            {
                if (LoadUserComplete != null)
                    LoadUserComplete(this, new LoadUserOperationEventArgs(loadUserOperation));
            }
        }

        private void LoginOperation_Completed(LoginOperation loginOperation)
        {
            if (loginOperation.LoginSuccess)
            {
                if (LoginComplete != null)
                    LoginComplete(this, new LoginOperationEventArgs(loginOperation));
            }
            else
            {
                if (loginOperation.HasError)
                {
                    if (LoginComplete != null)
                        LoginComplete(this, new LoginOperationEventArgs(loginOperation, loginOperation.Error));
                    loginOperation.MarkErrorAsHandled();
                }
                else if (!loginOperation.IsCanceled)
                {
                    if (LoginComplete != null)
                    {
                        var ex = new Exception(CommonResources.BadUserOrPassword);
                        LoginComplete(this, new LoginOperationEventArgs(ex));
                    }
                }
                else
                {
                    if (LoginComplete != null)
                        LoginComplete(this, new LoginOperationEventArgs(loginOperation));
                }
            }
        }

        private void LogoutOperation_Completed(LogoutOperation logoutOperation)
        {
            if (logoutOperation.HasError)
            {
                if (LogoutComplete != null)
                    LogoutComplete(this, new LogoutOperationEventArgs(logoutOperation, logoutOperation.Error));
                logoutOperation.MarkErrorAsHandled();
            }
            else
            {
                if (LogoutComplete != null)
                    LogoutComplete(this, new LogoutOperationEventArgs(logoutOperation));
            }
        }
        #endregion "Private Methods"
    }
}
